package com.beemans.corekit.localpush;

import android.Manifest;
import android.app.KeyguardManager;
import android.app.LocaleManager;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.PowerManager;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.app.NotificationManagerCompat;
import androidx.core.content.ContextCompat;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.ProcessLifecycleOwner;

import java.util.Calendar;
import java.util.Locale;

/**
 * @noinspection ALL
 */
public final class NotificationScheduler {
    private static NotificationScheduler instance;

    private static final String TAG = NotificationScheduler.class.getSimpleName();
    private static final String COUNTRY_KOREA = "KR";
    private static final long ONE_HOUR = 60 * 60 * 1000L;

    // 线上
    private static final long LOCAL_CONFLICT_WITH_OTHER_MS = 60 * 1000L;            // 本地推送与其他推送的冲突
    private static final long LOCAL_DELAY_AFTER_CONFLICT_MS = 30 * 60 * 1000L;      // 本地冲突后延迟调度时间
    private static final long LISTENER_CONFLICT_WITH_OTHER_MS = 15 * 1000L;         // 监听通知冲突阈值
    private static final long PACKAGE_CONFLICT_WITH_OTHER_MS = 5 * 1000L;           // 安装/卸载通知冲突阈值
    private static final long PACKAGE_MIN_INTERVAL_MS = 30 * 1000L;                 // 安装/卸载通知最小间隔
    private static final long LOCAL_OTHER_MIN_INTERVAL_MS = 1 * 60 * 60 * 1000L;    // 本地通知其他地区最小间隔
    private static final long LOCAL_KOREA_MIN_INTERVAL_MS = 2 * 60 * 60 * 1000L;    // 本地通知韩国地区最小间隔
    private static final long LISTENER_OTHER_MIN_INTERVAL_MS = 2 * 60 * 60 * 1000L; // 拦截监听通知其他地区最小间隔
    private static final long LISTENER_KOREA_MIN_INTERVAL_MS = 3 * 60 * 60 * 1000L; // 拦截监听通知韩国地区最小间隔

    // 线下
//    private static final long LOCAL_CONFLICT_WITH_OTHER_MS = 60 * 1000L;            // 本地推送与其他推送的冲突
//    private static final long LOCAL_DELAY_AFTER_CONFLICT_MS = 30 * 60 * 1000L;      // 本地冲突后延迟调度时间
//    private static final long LISTENER_CONFLICT_WITH_OTHER_MS = 15 * 1000L;         // 监听通知冲突阈值
//    private static final long PACKAGE_CONFLICT_WITH_OTHER_MS = 5 * 1000L;           // 安装/卸载通知冲突阈值
//    private static final long PACKAGE_MIN_INTERVAL_MS = 30 * 1000L;                 // 安装/卸载通知最小间隔
//    private static final long LOCAL_OTHER_MIN_INTERVAL_MS = 1 * 60 * 60 * 1000L;    // 本地通知其他地区最小间隔
//    private static final long LOCAL_KOREA_MIN_INTERVAL_MS = 2 * 60 * 60 * 1000L;    // 本地通知韩国地区最小间隔
//    private static final long LISTENER_OTHER_MIN_INTERVAL_MS = 2 * 60 * 60 * 1000L; // 拦截监听通知其他地区最小间隔
//    private static final long LISTENER_KOREA_MIN_INTERVAL_MS = 3 * 60 * 60 * 1000L; // 拦截监听通知韩国地区最小间隔

    private final long localInterval;    // 本地通知间隔
    private final long listenerInterval; // 监听通知间隔

    private final int maxLocalPerDay;    // 本地通知每日上限
    private final int maxListenerPerDay; // 监听通知每日上限
    private final int maxPackagePerDay = 2; // 安装卸载每日上限

    private long timeA; // 本地通知上次展示时间
    private long timeB; // 监听通知上次展示时间
    private long timeC; // 安装卸载通知上次展示时间

    private long localNextAllowedTime = 0;      // 本地通知最早允许展示时间
    private long listenerNextAllowedTime = 0;   // 监听通知最早允许展示时间
    private long packageNextAllowedTime = 0;    // 安装卸载通知最早允许展示时间

    private int localCountToday;      // 本地通知当日计数
    private int listenerCountToday;   // 监听通知当日计数
    private int packageCountToday;    // 安装卸载当日计数
    private int lastDay;              // 用于判断是否新的一天

    @NonNull
    private final HandlerThread handlerThread;
    @NonNull
    private final Handler handler;
    @NonNull
    private final Context context;
    @NonNull
    private final SharedPreferences prefs;

    @Nullable
    private Runnable localNotificationRunnable = null;
    @Nullable
    private Runnable listenerNotificationRunnable = null;
    @Nullable
    private Runnable packageNotificationRunnable = null;

    // SharedPreferences Key
    private static final String PREF_NAME_NOTIFICATION_SCHEDULER = "PREF_NAME_NOTIFICATION_SCHEDULER";
    private static final String KEY_TIME_A = "KEY_TIME_A";
    private static final String KEY_TIME_B = "KEY_TIME_B";
    private static final String KEY_TIME_C = "KEY_TIME_C";
    private static final String KEY_LOCAL_COUNT = "KEY_LOCAL_COUNT";
    private static final String KEY_LISTENER_COUNT = "KEY_LISTENER_COUNT";
    private static final String KEY_PACKAGE_COUNT = "KEY_PACKAGE_COUNT";
    private static final String KEY_LAST_DAY = "KEY_LAST_DAY";

    @Nullable
    private NotificationCallback notificationCallback;

    public void setNotificationCallback(@NonNull NotificationCallback callback) {
        this.notificationCallback = callback;
    }

    // 私有构造
    private NotificationScheduler(@NonNull Context context) {
        this.context = context.getApplicationContext();
        this.prefs = context.getSharedPreferences(PREF_NAME_NOTIFICATION_SCHEDULER, Context.MODE_PRIVATE);

        // 根据系统语言/地区设置通知间隔和每日上限
        final Locale systemLocale = getSystemLanguage(context);
        final String country = systemLocale.getCountry();
        if (COUNTRY_KOREA.equalsIgnoreCase(country)) {
            localInterval = LOCAL_KOREA_MIN_INTERVAL_MS;
            listenerInterval = LISTENER_KOREA_MIN_INTERVAL_MS;
            maxLocalPerDay = 4;
            maxListenerPerDay = 2;
        } else {
            localInterval = LOCAL_OTHER_MIN_INTERVAL_MS;
            listenerInterval = LISTENER_OTHER_MIN_INTERVAL_MS;
            maxLocalPerDay = 8;
            maxListenerPerDay = 3;
        }

        // 初始化数据
        timeA = prefs.getLong(KEY_TIME_A, 0);
        timeB = prefs.getLong(KEY_TIME_B, 0);
        timeC = prefs.getLong(KEY_TIME_C, 0);
        localCountToday = prefs.getInt(KEY_LOCAL_COUNT, 0);
        listenerCountToday = prefs.getInt(KEY_LISTENER_COUNT, 0);
        packageCountToday = prefs.getInt(KEY_PACKAGE_COUNT, 0);
        lastDay = prefs.getInt(KEY_LAST_DAY, -1);

        handlerThread = new HandlerThread(TAG);
        handlerThread.start();
        handler = new Handler(handlerThread.getLooper());
    }

    public static synchronized NotificationScheduler getInstance(Context context) {
        if (instance == null) {
            instance = new NotificationScheduler(context);
        }
        return instance;
    }

    // -------------------------
    // 检查通知逻辑
    // -------------------------

    /**
     * 安装、卸载监听调用
     */
    public void checkPackageWatchNotification(@Nullable InstalledAppInfo appInfo, @NonNull PackageEventType type) {
        handler.post(() -> {
            final long now = System.currentTimeMillis();
            refreshDailyCount();

            if (!isWithinPackageTimeWindow()) return;
            if (!canShowNotification() || !isScreenUnlocked() || isAppForeground()) return;

            if (packageCountToday >= maxPackagePerDay) return;
            if (now - timeC < PACKAGE_MIN_INTERVAL_MS) return;

            final boolean conflictWithLocal = (timeA > 0 && now - timeA < PACKAGE_CONFLICT_WITH_OTHER_MS);
            final boolean conflictWithListener = (timeB > 0 && now - timeB < PACKAGE_CONFLICT_WITH_OTHER_MS);
            if (conflictWithLocal || conflictWithListener) {
                if (packageNotificationRunnable == null) {
                    packageNextAllowedTime = now + PACKAGE_CONFLICT_WITH_OTHER_MS;
                }
                schedulePackageNotificationDelay(appInfo, type, PACKAGE_CONFLICT_WITH_OTHER_MS);
                return;
            }

            if (now < packageNextAllowedTime) return;

            cancelPendingPackageNotification();

            showPackageWatchNotification(appInfo, type);
            timeC = now;
            packageCountToday++;
            persistPackageData();
        });
    }

    /**
     * 本地推送调用
     */
    public void checkLocalNotification() {
        handler.post(() -> {
            final long now = System.currentTimeMillis();
            refreshDailyCount();

            if (!isWithinLocalTimeWindow()) return;
            if (!canShowNotification() || !isScreenUnlocked() || isAppForeground()) return;

            if (localCountToday >= maxLocalPerDay) return;
            if (now - timeA < localInterval) return;

            final boolean conflictWithListener = (timeB > 0 && now - timeB < LOCAL_CONFLICT_WITH_OTHER_MS);
            final boolean conflictWithPackageWatch = (timeC > 0 && now - timeC < LOCAL_CONFLICT_WITH_OTHER_MS);
            if (conflictWithListener || conflictWithPackageWatch) {
                if (localNotificationRunnable == null) {
                    localNextAllowedTime = now + LOCAL_DELAY_AFTER_CONFLICT_MS;
                }
                scheduleLocalNotificationDelay(LOCAL_DELAY_AFTER_CONFLICT_MS);
                return;
            }

            if (now < localNextAllowedTime) return;

            cancelPendingLocalNotification();

            showLocalNotification();
            timeA = now;
            localCountToday++;
            persistLocalData();
        });
    }

    /**
     * 拦截监听调用
     */
    public void checkListenerNotification() {
        handler.post(() -> {
            final long now = System.currentTimeMillis();
            refreshDailyCount();

            if (!isWithinListenerTimeWindow()) return;
            if (!canShowNotification() || !isScreenUnlocked() || isAppForeground()) return;

            if (listenerCountToday >= maxListenerPerDay) return;
            if (now - timeB < listenerInterval) return;

            final boolean conflictWithLocal = (timeA > 0 && now - timeA < LISTENER_CONFLICT_WITH_OTHER_MS);
            final boolean conflictWithPackageWatch = (timeC > 0 && now - timeC < LISTENER_CONFLICT_WITH_OTHER_MS);
            if (conflictWithLocal || conflictWithPackageWatch) {
                if (listenerNotificationRunnable == null) {
                    listenerNextAllowedTime = now + LISTENER_CONFLICT_WITH_OTHER_MS;
                }
                scheduleListenerNotificationDelay(LISTENER_CONFLICT_WITH_OTHER_MS);
                return;
            }

            if (now < listenerNextAllowedTime) return;

            cancelPendingListenerNotification();

            showListenerNotification();
            timeB = now;
            listenerCountToday++;
            persistListenerData();
        });
    }

    // -------------------------
    // 延迟任务调度
    // -------------------------

    /**
     * @noinspection SameParameterValue
     */
    private void schedulePackageNotificationDelay(
            @Nullable InstalledAppInfo appInfo,
            @NonNull PackageEventType type,
            long delayMillis
    ) {
        if (packageNotificationRunnable == null) {
            packageNotificationRunnable = () -> {
                packageNotificationRunnable = null;
                checkPackageWatchNotification(appInfo, type);
            };
            handler.postDelayed(packageNotificationRunnable, delayMillis);
        }
    }

    /**
     * @noinspection SameParameterValue
     */
    private void scheduleLocalNotificationDelay(long delayMillis) {
        if (localNotificationRunnable == null) {
            localNotificationRunnable = () -> {
                localNotificationRunnable = null;
                checkLocalNotification();
            };
            handler.postDelayed(localNotificationRunnable, delayMillis);
        }
    }

    /**
     * @noinspection SameParameterValue
     */
    private void scheduleListenerNotificationDelay(long delayMillis) {
        if (listenerNotificationRunnable == null) {
            listenerNotificationRunnable = () -> {
                listenerNotificationRunnable = null;
                checkListenerNotification();
            };
            handler.postDelayed(listenerNotificationRunnable, delayMillis);
        }
    }

    private void cancelPendingPackageNotification() {
        if (packageNotificationRunnable != null) {
            handler.removeCallbacks(packageNotificationRunnable);
            packageNotificationRunnable = null;
        }
    }

    private void cancelPendingLocalNotification() {
        if (localNotificationRunnable != null) {
            handler.removeCallbacks(localNotificationRunnable);
            localNotificationRunnable = null;
        }
    }

    private void cancelPendingListenerNotification() {
        if (listenerNotificationRunnable != null) {
            handler.removeCallbacks(listenerNotificationRunnable);
            listenerNotificationRunnable = null;
        }
    }

    // -------------------------
    // 展示通知接口
    // -------------------------

    private void showPackageWatchNotification(@Nullable InstalledAppInfo appInfo, @NonNull PackageEventType type) {
        if (notificationCallback != null) {
            notificationCallback.onShowPackageWatchNotification(appInfo, type);
        }
    }

    private void showLocalNotification() {
        if (notificationCallback != null) {
            notificationCallback.onShowLocalNotification();
        }
    }

    private void showListenerNotification() {
        if (notificationCallback != null) {
            notificationCallback.onShowListenerNotification();
        }
    }

    // -------------------------
    // 日切重置计数
    // -------------------------

    private int getCurrentDateInt() {
        final Calendar cal = Calendar.getInstance();
        final int year = cal.get(Calendar.YEAR);
        final int month = cal.get(Calendar.MONTH) + 1;
        final int day = cal.get(Calendar.DAY_OF_MONTH);
        return year * 10000 + month * 100 + day; // yyyyMMdd
    }

    private void refreshDailyCount() {
        final int todayInt = getCurrentDateInt();
        if (todayInt != lastDay) {
            lastDay = todayInt;

            // 清计数
            localCountToday = 0;
            listenerCountToday = 0;
            packageCountToday = 0;

            // 清时间戳（表示“很久之前/未发送过”）
            timeA = 0L;
            timeB = 0L;
            timeC = 0L;

            // 清 nextAllowed（避免跨天延迟）
            localNextAllowedTime = 0L;
            listenerNextAllowedTime = 0L;
            packageNextAllowedTime = 0L;

            // 取消任何 pending 的延迟任务（避免它们在新的一天触发）
            cancelPendingLocalNotification();
            cancelPendingListenerNotification();
            cancelPendingPackageNotification();

            persistDailyCount();
        }
    }

    // -------------------------
    // 持久化方法
    // -------------------------

    private void persistPackageData() {
        prefs.edit()
                .putLong(KEY_TIME_C, timeC)
                .putInt(KEY_PACKAGE_COUNT, packageCountToday)
                .apply();
    }

    private void persistLocalData() {
        prefs.edit()
                .putLong(KEY_TIME_A, timeA)
                .putInt(KEY_LOCAL_COUNT, localCountToday)
                .apply();
    }

    private void persistListenerData() {
        prefs.edit()
                .putLong(KEY_TIME_B, timeB)
                .putInt(KEY_LISTENER_COUNT, listenerCountToday)
                .apply();
    }

    private void persistDailyCount() {
        prefs.edit()
                .putInt(KEY_LOCAL_COUNT, localCountToday)
                .putInt(KEY_LISTENER_COUNT, listenerCountToday)
                .putInt(KEY_PACKAGE_COUNT, packageCountToday)
                .putInt(KEY_LAST_DAY, lastDay)
                .putLong(KEY_TIME_A, timeA)
                .putLong(KEY_TIME_B, timeB)
                .putLong(KEY_TIME_C, timeC)
                .apply();
    }

    // -------------------------
    // 前提条件判断
    // -------------------------

    /**
     * @noinspection BooleanMethodIsAlwaysInverted
     */
    private boolean canShowNotification() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
            final String permission = Manifest.permission.POST_NOTIFICATIONS;
            return ContextCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED;
        } else {
            return NotificationManagerCompat.from(context).areNotificationsEnabled();
        }
    }

    /**
     * @noinspection BooleanMethodIsAlwaysInverted
     */
    private boolean isScreenUnlocked() {
        boolean isDeviceUnlocked = true;
        try {
            final KeyguardManager keyguardManager = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE);
            isDeviceUnlocked = !keyguardManager.isKeyguardLocked();  // 返回 false 表示屏幕解锁
        } catch (Exception ignored) {
        }
        if (isDeviceUnlocked) {
            try {
                final PowerManager manager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
                isDeviceUnlocked = manager.isInteractive();
            } catch (Exception ignored) {
            }
        }

        return isDeviceUnlocked;
    }

    /**
     * @noinspection BooleanMethodIsAlwaysInverted
     */
    private boolean isAppForeground() {
        return ProcessLifecycleOwner.get().getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.RESUMED);
    }

    // -------------------------
    // 时间段判断
    // -------------------------

    private boolean isWithinPackageTimeWindow() {
        final Calendar cal = Calendar.getInstance();
        final int hour = cal.get(Calendar.HOUR_OF_DAY);
        return hour >= 9 && hour <= 22; // 09:00 <= hour <= 22:00
    }

    private boolean isWithinLocalTimeWindow() {
        final Calendar cal = Calendar.getInstance();
        final int hour = cal.get(Calendar.HOUR_OF_DAY);
        return hour >= 8 && hour <= 22; // 08:00 <= hour <= 22:00
    }

    private boolean isWithinListenerTimeWindow() {
        final Calendar cal = Calendar.getInstance();
        final int hour = cal.get(Calendar.HOUR_OF_DAY);
        return hour >= 9 && hour <= 22; // 09:00 <= hour <= 22:00
    }

    // -------------------------
    // 资源释放
    // -------------------------
    public void release() {
        cancelPendingLocalNotification();
        cancelPendingListenerNotification();
        cancelPendingPackageNotification();

        handlerThread.quitSafely();
        instance = null;
    }

    // 获取系统默认语言/地区
    private static Locale getSystemLanguage(@NonNull Context context) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
            try {
                final LocaleManager localeManager = context.getSystemService(LocaleManager.class);
                return localeManager.getSystemLocales().get(0);
            } catch (Throwable ignored) {
                return Locale.getDefault();
            }
        }
        try {
            final Configuration config = Resources.getSystem().getConfiguration();
            return config.getLocales().get(0);
        } catch (Throwable ignored) {
            return Locale.getDefault();
        }
    }
}